Python(Tornado Tornado+uvloop Tornado+uvloop2 Flask Sanic Django) Go(http Httprouter Fasthttp) RPS性能对比

在v2ex上发现了一个Python的新web框架, Sanic, 基于uvloop, 其在GitHub上的性能数据十分耀眼, 于是想跟其他用过的web框架对比一下性能, 跑个分嘛.

测试环境:

Python:3.6.0 Go:1.8.0

测试脚本:

app用tmux启动, 然后使用Apache bench测试, n=1000, c=100, 每个app测5次

for session in test_tornado test_tornadouv test_tornadouv2 test_flask test_sanic
do
tmux kill-session -t $session > /dev/null 2>&1
sleep 1
tmux new -s $session -d "/root/.pyenv/versions/3.6.0/bin/python $session.py"
done
# django
tmux kill-session -t test_django
sleep 1
tmux new -s test_django -d "/root/.pyenv/versions/3.6.0/bin/python test_django/manage.py runserver 127.0.0.1:8893"

for session in test_go_http test_go_httprouter test_go_fasthttp
do
tmux kill-session -t $session
sleep 1
tmux new -s $session -d "go run $session.go"
done


for ((i=0;i<5;i++))
do
echo $i
ab -n 1000 -c 100 http://127.0.0.1:8888/ 2> /dev/null |grep "Requests per second" |awk '{printf "tornado-epoll :%s\n",$0}'
ab -n 1000 -c 100 http://127.0.0.1:8889/ 2> /dev/null |grep "Requests per second" |awk '{printf "tornado-uvloop :%s\n",$0}'
ab -n 1000 -c 100 http://127.0.0.1:8890/ 2> /dev/null |grep "Requests per second" |awk '{printf "tornado-uvloop2:%s\n",$0}'
ab -n 1000 -c 100 http://127.0.0.1:8891/ 2> /dev/null |grep "Requests per second" |awk '{printf "flask :%s\n",$0}'
ab -n 1000 -c 100 http://127.0.0.1:8892/ 2> /dev/null |grep "Requests per second" |awk '{printf "sanic :%s\n",$0}'
ab -n 1000 -c 10 http://127.0.0.1:8893/ 2> /dev/null |grep "Requests per second" |awk '{printf "django :%s\n",$0}' # django自带的wsgi server 无法处理concurrent 100
ab -n 1000 -c 100 http://127.0.0.1:8894/ 2> /dev/null |grep "Requests per second" |awk '{printf "go_http :%s\n",$0}'
ab -n 1000 -c 100 http://127.0.0.1:8895/ 2> /dev/null |grep "Requests per second" |awk '{printf "go_httprouter :%s\n",$0}'
ab -n 1000 -c 100 http://127.0.0.1:8896/ 2> /dev/null |grep "Requests per second" |awk '{printf "go_fasthttp :%s\n",$0}'
done

测试结果:

本机 i5-6600k@3.5GHZ 四核16GB

0
tornado-epoll :Requests per second: 1714.60 [#/sec] (mean)
tornado-uvloop :Requests per second: 1988.30 [#/sec] (mean)
tornado-uvloop2:Requests per second: 1977.77 [#/sec] (mean)
flask :Requests per second: 2167.29 [#/sec] (mean)
sanic :Requests per second: 14221.72 [#/sec] (mean)
django :Requests per second: 1157.64 [#/sec] (mean)
go_http :Requests per second: 13847.92 [#/sec] (mean)
go_httprouter :Requests per second: 14595.34 [#/sec] (mean)
go_fasthttp :Requests per second: 12730.74 [#/sec] (mean)
1
tornado-epoll :Requests per second: 2011.02 [#/sec] (mean)
tornado-uvloop :Requests per second: 1788.50 [#/sec] (mean)
tornado-uvloop2:Requests per second: 1980.40 [#/sec] (mean)
flask :Requests per second: 2237.84 [#/sec] (mean)
sanic :Requests per second: 13614.70 [#/sec] (mean)
django :Requests per second: 1145.03 [#/sec] (mean)
go_http :Requests per second: 13811.01 [#/sec] (mean)
go_httprouter :Requests per second: 13441.94 [#/sec] (mean)
go_fasthttp :Requests per second: 12619.25 [#/sec] (mean)
2
tornado-epoll :Requests per second: 2056.31 [#/sec] (mean)
tornado-uvloop :Requests per second: 1831.49 [#/sec] (mean)
tornado-uvloop2:Requests per second: 2015.39 [#/sec] (mean)
flask :Requests per second: 2124.87 [#/sec] (mean)
sanic :Requests per second: 12485.80 [#/sec] (mean)
django :Requests per second: 1160.55 [#/sec] (mean)
go_http :Requests per second: 14014.43 [#/sec] (mean)
go_httprouter :Requests per second: 13949.34 [#/sec] (mean)
go_fasthttp :Requests per second: 12419.12 [#/sec] (mean)
3
tornado-epoll :Requests per second: 2017.32 [#/sec] (mean)
tornado-uvloop :Requests per second: 1731.04 [#/sec] (mean)
tornado-uvloop2:Requests per second: 2014.46 [#/sec] (mean)
flask :Requests per second: 2100.02 [#/sec] (mean)
sanic :Requests per second: 12459.04 [#/sec] (mean)
django :Requests per second: 1164.06 [#/sec] (mean)
go_http :Requests per second: 13239.77 [#/sec] (mean)
go_httprouter :Requests per second: 14127.49 [#/sec] (mean)
go_fasthttp :Requests per second: 12824.46 [#/sec] (mean)
4
tornado-epoll :Requests per second: 1810.77 [#/sec] (mean)
tornado-uvloop :Requests per second: 1975.12 [#/sec] (mean)
tornado-uvloop2:Requests per second: 1962.81 [#/sec] (mean)
flask :Requests per second: 2234.08 [#/sec] (mean)
sanic :Requests per second: 13881.95 [#/sec] (mean)
django :Requests per second: 1154.90 [#/sec] (mean)
go_http :Requests per second: 14399.47 [#/sec] (mean)
go_httprouter :Requests per second: 13688.69 [#/sec] (mean)
go_fasthttp :Requests per second: 12672.99 [#/sec] (mean)

阿里云ECS 双核8GB

0
tornado-epoll :Requests per second: 542.60 [#/sec] (mean)
tornado-uvloop :Requests per second: 930.95 [#/sec] (mean)
tornado-uvloop2:Requests per second: 811.54 [#/sec] (mean)
flask :Requests per second: 897.00 [#/sec] (mean)
sanic :Requests per second: 5681.50 [#/sec] (mean)
django :Requests per second: 447.41 [#/sec] (mean)
go_http :Requests per second: 8551.98 [#/sec] (mean)
go_httprouter :Requests per second: 9655.31 [#/sec] (mean)
go_fasthttp :Requests per second: 11469.21 [#/sec] (mean)
1
tornado-epoll :Requests per second: 969.97 [#/sec] (mean)
tornado-uvloop :Requests per second: 970.14 [#/sec] (mean)
tornado-uvloop2:Requests per second: 990.24 [#/sec] (mean)
flask :Requests per second: 900.50 [#/sec] (mean)
sanic :Requests per second: 5789.89 [#/sec] (mean)
django :Requests per second: 435.28 [#/sec] (mean)
go_http :Requests per second: 8076.63 [#/sec] (mean)
go_httprouter :Requests per second: 9340.99 [#/sec] (mean)
go_fasthttp :Requests per second: 6024.82 [#/sec] (mean)
2
tornado-epoll :Requests per second: 946.98 [#/sec] (mean)
tornado-uvloop :Requests per second: 975.64 [#/sec] (mean)
tornado-uvloop2:Requests per second: 955.25 [#/sec] (mean)
flask :Requests per second: 1021.28 [#/sec] (mean)
sanic :Requests per second: 6361.36 [#/sec] (mean)
django :Requests per second: 440.23 [#/sec] (mean)
go_http :Requests per second: 7601.67 [#/sec] (mean)
go_httprouter :Requests per second: 8123.21 [#/sec] (mean)
go_fasthttp :Requests per second: 8500.00 [#/sec] (mean)
3
tornado-epoll :Requests per second: 944.39 [#/sec] (mean)
tornado-uvloop :Requests per second: 921.12 [#/sec] (mean)
tornado-uvloop2:Requests per second: 980.30 [#/sec] (mean)
flask :Requests per second: 946.53 [#/sec] (mean)
sanic :Requests per second: 5686.99 [#/sec] (mean)
django :Requests per second: 498.31 [#/sec] (mean)
go_http :Requests per second: 6297.78 [#/sec] (mean)
go_httprouter :Requests per second: 8078.91 [#/sec] (mean)
go_fasthttp :Requests per second: 6523.84 [#/sec] (mean)
4
tornado-epoll :Requests per second: 953.86 [#/sec] (mean)
tornado-uvloop :Requests per second: 983.97 [#/sec] (mean)
tornado-uvloop2:Requests per second: 955.04 [#/sec] (mean)
flask :Requests per second: 994.77 [#/sec] (mean)
sanic :Requests per second: 6877.11 [#/sec] (mean)
django :Requests per second: 510.76 [#/sec] (mean)
go_http :Requests per second: 8243.14 [#/sec] (mean)
go_httprouter :Requests per second: 7055.97 [#/sec] (mean)
go_fasthttp :Requests per second: 6183.83 [#/sec] (mean)

###结论:

  1. Sanic确实叼, 拉出Flask一大截. 由于这个测试只是简单返回”Hello, world”, Tornado的优势要在使用了aio,AsyncHTTPClient的时候才能发挥出来. 如果拿tornado当纯同步使用的话, 性能会比flask弱一点点.
  2. Django自带的wsgi server无法完成此任务.
  3. fasthttp表现的很奇怪, 可能是handler太简单的情况下sync/pool的代价超过了gc的影响, httprouter比http快一点.

###测试代码:

1.test_tornado.py Tornado 4.4.2

import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")


def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])


if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

2.test_tornadouv.py Tornado+uvloop(pip install tornadouvloop)

import tornado.ioloop
import tornado.web
from tornadouvloop import TornadoUvloop


class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")


def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])


if __name__ == "__main__":
app = make_app()
app.listen(8889)
tornado.ioloop.IOLoop.configure(TornadoUvloop)
tornado.ioloop.IOLoop.current().start()

3.test_tornadouv2.py Tornado+uvloop2(pip install tornaduv)

import tornado.ioloop
import tornado.web
from tornaduv import UVLoop


class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")


def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])


if __name__ == "__main__":
app = make_app()
app.listen(8890)
tornado.ioloop.IOLoop.configure(UVLoop)
tornado.ioloop.IOLoop.current().start()

4.test_flask.py Flask 0.12

from flask import Flask

app = Flask(__name__)


@app.route("/")
def hello():
return "Hello, world"


if __name__ == "__main__":
app.run(port=8891)

5.test_sanic.py Sanic 0.4.1

from sanic import Sanic
from sanic.response import HTTPResponse

app = Sanic()


@app.route("/")
async def test(request):
return HTTPResponse("Hello, world")


if __name__ == "__main__":
app.run(port=8892)

6.test_django.py Django

# views.py
from django.http import HttpResponse


def hello(request):
return HttpResponse("Hello, world")

7.test_go_http.go Go http

package main

import (
"net/http"
"runtime"
)

func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, world"))
})
http.ListenAndServe("127.0.0.1:8894", nil)
}

8.test_go_httprouter.go GO httprouter

package main

import (
"net/http"
"github.com/julienschmidt/httprouter"
"runtime"
)

func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
router := httprouter.New()
router.HandlerFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, world"))
})
http.ListenAndServe("127.0.0.1:8895", router)
}

9.test_go_fasthttp.go Go fasthttp

package main

import (
"github.com/valyala/fasthttp"
"runtime"
)

func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
requestHandler := func(ctx *fasthttp.RequestCtx) {
ctx.Write([]byte("Hello, world"))
}
fasthttp.ListenAndServe("127.0.0.1:8896", requestHandler)
}